﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

/*
 AsyncSocket1项目：主要使用原生态的异步方式（.netframework4.5以后可以使用async/await关键字实现异步，编程会更加方便和简洁）
    来实现Socket对话，解决了 Socket2项目 客户端发送一句，服务端才能发送一句的问题。
 PS:代码效果是客户端和服务端 一对一的对话效果（了解原理后，再去实现 1对多，多对多的对话）
 */
namespace AsyncSocket1.Server
{
    class Program
    {
        static Socket receiveSocket = null;
        static byte[] receiveByte = null;
        static void Main(string[] args)
        {
            //设置服务端监听的IP地址
            IPEndPoint IP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5001);
            //初始化Socket
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //绑定IP地址
            serverSocket.Bind(IP);
            //监听IP地址（设置监听队列为8）
            serverSocket.Listen(8);
            Console.WriteLine("服务已启动...");
            //开始接收Socket连接（此处为异步方法，此处不会阻塞进程来等待客户端的连接）
            serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), serverSocket);
            Console.WriteLine("等待连接中...");
            //接收信息
            while (true)
            {
                if (receiveSocket != null)
                {//保证接收到了客户端的Socket请求，再启动接收方法
                    receiveByte = new byte[1024];
                    receiveSocket.BeginReceive(receiveByte, 0, receiveByte.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), receiveSocket);
                    break;
                }
                Thread.Sleep(50);
            }

            //发送信息
            while (true)
            {
                //输入数据
                Console.WriteLine("请输入(输入exit退出对话)：");
                string inputMsg = Console.ReadLine();
                if (inputMsg.ToLower() == "exit")
                {
                    break;
                }
                byte[] sendByte = Encoding.UTF8.GetBytes(inputMsg);
                receiveSocket.BeginSend(sendByte, 0, sendByte.Length, SocketFlags.None, new AsyncCallback(SendCallBack), receiveSocket);
            }

            Console.WriteLine("按任意键退出控制台...");
            Console.Read();
        }

        static void AcceptCallBack(IAsyncResult iar)
        {
            Socket serverSocket = iar.AsyncState as Socket;
            receiveSocket = serverSocket.EndAccept(iar);
            Console.WriteLine("连接成功！！客户端地址：{0}", receiveSocket.RemoteEndPoint);
        }

        static void ReceiveCallBack(IAsyncResult iar)
        {
            Socket tmpReceiveSocket = iar.AsyncState as Socket;
            tmpReceiveSocket.EndReceive(iar);

            //如果数据没有1024个字节，会出现很多空格，所以需要TrimEnd去除
            string receiveMsg = Encoding.UTF8.GetString(receiveByte).TrimEnd('\0');
            Console.WriteLine("客户端说:\n{0}", receiveMsg);
            //因为接受信息后，会顶掉之前的“请输入(输入exit退出对话)：”提示，所以此处再加一句
            Console.WriteLine("请输入(输入exit退出对话)：");
            //清空字节数组（思考下为什么要清空？不清空会出现什么情况呢？）
            receiveByte = new byte[1024];
            receiveSocket.BeginReceive(receiveByte, 0, receiveByte.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), receiveSocket);
        }

        static void SendCallBack(IAsyncResult iar)
        {
            Socket tmpReceiveSocket = iar.AsyncState as Socket;
            tmpReceiveSocket.EndSend(iar);
        }

    }
}
